package com.minivision.fdiot.service;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collector;
import java.util.stream.Collectors;

import com.google.gson.Gson;
import com.minivision.fdiot.entity.DeviceFaceSet;
import com.minivision.fdiot.entity.FaceSet;
import com.minivision.fdiot.repository.mysql.DeviceFaceSetRepository;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import com.minivision.fdiot.common.ChunkRequest;
import com.minivision.fdiot.entity.Device;
import com.minivision.fdiot.mqtt.MqttConnectionManager;
import com.minivision.fdiot.mqtt.protocol.ActiveCodeUtil;
import com.minivision.fdiot.repository.mysql.DeviceRepository;
import com.minivision.fdiot.rest.param.device.CreateDeviceParam;
import com.minivision.fdiot.rest.param.device.QueryDeviceParam;
import com.minivision.fdiot.rest.param.device.UpdateDeviceParam;
import com.minivision.fdiot.util.BeanPropertyUtils;

@Service
public class DeviceServiceImpl extends BaseService implements DeviceService {
  
  @Autowired
  private DeviceRepository deviceRepo;

  @Autowired
  private DeviceFaceSetRepository deviceFaceSetRepository;
  
  @Autowired
  private ActiveCodeUtil activeCodeUtil;
  
  @Autowired
  private MqttConnectionManager connectionManager;
  
  @Override
  public Device createDevice(CreateDeviceParam param) {
    Device device = new Device();
    BeanUtils.copyProperties(param, device);
    return deviceRepo.saveAndFlush(device);
  }

  @Override
  public List<Device> createBatch(Iterable<Device> devices) {
    return deviceRepo.save(devices);
  }

  @Override
  public Device updateDevice(UpdateDeviceParam param) {
    Device existed = deviceRepo.findBySn(param.getSn());
    if(param.getFacesetTokenMap() != null && param.getFacesetTokenMap().size() > 0){
      Map<String,DeviceFaceSet> facesetTokenMap = param.getFacesetTokenMap();
      deviceFaceSetRepository.deleteBySn(existed.getSn());
      deviceFaceSetRepository.save(facesetTokenMap.entrySet().stream().map(r -> r.getValue()).collect(Collectors.toList()));
    }
    BeanPropertyUtils.copyProperties(param, existed);
    return deviceRepo.saveAndFlush(existed);
  }

  @Override
  public void deleteDevice(Long deviceId) {
    Device device = deviceRepo.findOne(deviceId);
    if (device == null) {
      return;
    }
    //删除设备时断开与设备的连接
    connectionManager.closeConnection(device.getSn());
    deviceFaceSetRepository.deleteBySn(device.getSn());
    deviceRepo.delete(deviceId);

  }



  @Override
  public long deleteBatch(List<Long> deviceIds) {
    List<Device> devices = deviceRepo.findAll(deviceIds);
    if (CollectionUtils.isEmpty(devices)) {
      return 0;
    }
    //断开与设备的连接
    devices.stream().forEach((device) -> {
      connectionManager.closeConnection(device.getSn());
      //删除关联管理表
      deviceFaceSetRepository.deleteBySn(device.getSn());
    });
    long result = deviceRepo.deleteByIdIn(deviceIds);

    return result;
  }

  @Override
  public Page<Device> findByPage(QueryDeviceParam param) {
    ExampleMatcher matcher = ExampleMatcher.matching()
        .withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING)
        .withIgnoreCase(true);
    Device device = new Device();
    BeanUtils.copyProperties(param, device);
    Example<Device> example = Example.of(device, matcher);
    return deviceRepo.findAll(example, new ChunkRequest(param.getOffset(), param.getLimit(), new Sort(Direction.DESC, "updateTime")));
  }

  /**
   * 激活设备
   */
  @Override
  public void activateDevice(String deviceSn) {
    deviceRepo.activate(deviceSn);
  }
  
  /**
   * 设备上线
   */
  public void deviceOnline(String sn, String connectNode) {
    deviceRepo.online(sn, connectNode);
  }
  
  /**
   * 设备离线
   */
  public void deviceOffline(String sn, String connectNode) {
    deviceRepo.offline(sn, connectNode);
  }
  
  /**
   * 设备连接断开
   */
  public void deviceLost(String sn, String connectNode) {
    deviceRepo.offline(sn, connectNode);
  }
  
  public Device findDevice(String sn) {
    return deviceRepo.findBySn(sn);
  }

  /**
   * 获取设备激活码
   */
  @Override
  public String getActivationCode(String model, String sn) {
    return activeCodeUtil.generateActiveCode(model, sn);
  }

  /**
   * 根据sn去DeviceFaceSet表查询数据
   * @param sn
   * @return
   */
  @Override
  public List<DeviceFaceSet> findFaceSetBySn(String sn) {
    return deviceFaceSetRepository.findBySnOrderByPriorityAsc(sn);
  }

  @Override
  public List<Device> findByAppKey(String appKey) {
    return deviceRepo.findByAppKey(appKey);
  }

  @Override
  public Device save(Device device) {
    return deviceRepo.saveAndFlush(device);
  }

  @Override
  public List<Device> findDevices(List<String> deviceSns) {
    return deviceRepo.findBySnIn(deviceSns);
  }

  /*@Override
  public List<Device> findDevices(Device device) {
    ExampleMatcher matcher = ExampleMatcher.matching()
        .withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING)
        .withIgnoreCase(true);
    Example<Device> example = Example.of(device, matcher);
    return deviceRepo.findAll(example);
  }*/

  /**
   * 获取绑定某个人脸库的所有设备
   */
  @Override
  public List<Device> findByFaceSetToken(String facesetToken) {
    return deviceRepo.findByFacesetTokensContaining(facesetToken);
  }

  @Override
  public Device findById(Long id) {
    return deviceRepo.findOne(id);
  }

  @Override
  public void deleteDeviceBySn(String deviceSn) {

    //断开与设备的连接
    connectionManager.closeConnection(deviceSn);
    deviceFaceSetRepository.deleteBySn(deviceSn);
    deviceRepo.deleteBySn(deviceSn);

  }

  @Override
  public long deleteBatchBySn(Set<String> deviceSns) {

    //断开与设备的连接
    deviceSns.stream().forEach((deviceSn) -> {
      connectionManager.closeConnection(deviceSn);
      deviceFaceSetRepository.deleteBySn(deviceSn);
    });
    long result = deviceRepo.deleteBySnIn(deviceSns);

    return result;
  }

  /**
   * 获取某个应用下绑定了某个人脸库的所有设备
   */
  @Override
  public List<Device> findByAppKeyAndFaceSetToken(String appKey, String facesetToken) {
    return deviceRepo.findByAppKeyEqualsAndFacesetTokensContaining(appKey, facesetToken);
  }
  
}
